/*******************************************************************************
 * Open Battery is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the 
 * Free Software Foundation; either version 2 of the License, or (at your 
 * option) any later version.
 *
 * Gareth Jones  gljones@ic.ac.uk
 *
 * Some of the code in this project is from the Android Battery Dog. The 
 * license information for Android Battery Dog is copied below.
 *
 *******************************************************************************/

/*******************************************************************************
 * Copyright (c) 2009 Ferenc Hechler - ferenc_hechler@users.sourceforge.net
 * 
 * This file is part of the Android Battery Dog
 *
 * The Android Battery Dog is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation;
 * either version 2 of the License, or (at your option) any later version.
 * 
 * The Android Battery Dog is distributed
 * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with the Android Battery Dog;
 * if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *  
 *******************************************************************************/
package uk.ac.imperial.doc.aesop.openbattery;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

import java.io.FileWriter;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.widget.Toast;

import android.util.Log;

public class OpenBatteryService extends Service {


	public static final String LOGFILEPATH = "OpenBattery/battery.csv";
	public static final long MINSEND = 1000;

	private final static String[] batteryExtraKeys = { "level", "scale",
			"voltage", "temperature", "plugged", "status", "health" };

	private File mBatteryLogFile;
	private Intent mLastBatteryIntent;
	private boolean mQuitThread;
	private boolean mThreadRunning;

	@Override
	public void onCreate() {

		//Log.i("openbattery", "onCreate");

		super.onCreate();
		if (!mThreadRunning) {
			mLastBatteryIntent = null;
			mQuitThread = false;
			Thread thr = new Thread(null, mTask, "OpenBatteryService");
			thr.start();
			registerReceiver(mBatInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));

		}
	}

	@Override
	public void onDestroy() {
		mQuitThread = true;
		notifyService();

		super.onDestroy();
		unregisterReceiver(mBatInfoReceiver);

	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	// Check Internet connection, from
	// http://stackoverflow.com/questions/1560788/

	private boolean isNetworkAvailable() {
		ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo netInfo = cm.getActiveNetworkInfo();
		if (netInfo != null && netInfo.isConnected()) {
			return true;
		}
		return false;
	}

	// Check Wi-fi connectivity, from http://stackoverflow.com/questions/2973290/

	private boolean isWifiAvailable() {
		ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		State wifiState = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
				.getState();
		if (wifiState == NetworkInfo.State.CONNECTED) {
			return true;
		}
		return false;
	}

	private BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context ctx, Intent intent) {

			String submitted="";

			try {
				mLastBatteryIntent = (Intent) intent.clone();
				notifyService();

				// send data if charging and user has set preference to
				// automatically send data

				SharedPreferences settings = getSharedPreferences(OpenBattery.PREFS_NAME, 0);
				SharedPreferences.Editor editor = settings.edit();

				Integer autoSubmit = Integer.parseInt(settings.getString("autoSubmit", "2"));
				Integer registeredDevice = Integer.parseInt(settings.getString("registeredDevice", "0"));
				String deviceid = settings.getString("deviceid","");

	    		if (deviceid.equalsIgnoreCase("")) {
					
					deviceid = Long.toHexString(Double.doubleToLongBits(Math.random()));

		    		editor.putString("deviceid",deviceid);
		    		editor.commit();
					
				}

				// find out if device is plugged in
	    		IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
	    		Intent batteryStatus = ctx.registerReceiver(null, ifilter);
	    		
	    		int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
	    		boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
	    		                     status == BatteryManager.BATTERY_STATUS_FULL;
	    		
	    		
	    		// check if enough data has been logged for submission
	    		File file = new File(Environment.getExternalStorageDirectory(),
	    				OpenBatteryService.LOGFILEPATH);

	    		Long fileLength = file.length();
	    		
	    		boolean enoughLogged = false;
	    		
	    		if (fileLength > MINSEND) {
	    			
	    			enoughLogged = true;
	    			
	    		}
	    		
	    		
				// autoSubmit when charging
				if ((autoSubmit == 1) && isCharging
						&& (isNetworkAvailable()) && enoughLogged) { // && {

		        	Toast.makeText(getApplicationContext(), "autoSubmit first case", Toast.LENGTH_SHORT);

					
					if (registeredDevice == 0) {
						
						registerDevice();
						
					}
					
					submitted = submitData(deviceid);

				}

				// autoSubmit when charging and connected to Wifi
				if ((autoSubmit == 2) && isCharging && (isWifiAvailable()) && enoughLogged) {


		        	Toast.makeText(getApplicationContext(), "autoSubmit second case", Toast.LENGTH_SHORT);

					if (registeredDevice == 0) {
						
						registerDevice();
						
					}
					
					submitted = submitData(deviceid);

				}

			} catch (Exception e) {
			}
			
			Log.d("openbattery","BroadcastReceiver: " + submitted);
		}

	};

	private void registerDevice() {
		
		SharedPreferences settings = getSharedPreferences(OpenBattery.PREFS_NAME, 0);
		SharedPreferences.Editor editor = settings.edit();

		String deviceid = settings.getString("deviceid", "");
		
		try {
			
			HttpClient httpclient = new DefaultHttpClient();
     		HttpPost httpPostDeviceExists = new HttpPost("https://www.doc.ic.ac.uk/~gljones/openbattery/device-exists.php");
     		
     		List<NameValuePair> nameValuePairsDeviceExists = new ArrayList<NameValuePair>(2);
     		nameValuePairsDeviceExists.add(new BasicNameValuePair("fromdevice", "1"));
     		nameValuePairsDeviceExists.add(new BasicNameValuePair("obversion", "1.0"));
     		nameValuePairsDeviceExists.add(new BasicNameValuePair("deviceid", deviceid));
			httpPostDeviceExists.setEntity(new UrlEncodedFormEntity(nameValuePairsDeviceExists));
		
			HttpResponse responseDeviceExists = httpclient.execute(httpPostDeviceExists);

			String responseStr = URLEncoder.encode(org.apache.http.util.EntityUtils.toString(responseDeviceExists.getEntity()));

			//Log.d("openbattery","deviceexists? " + responseStr);
			
			// If device does not exist, create device

			if (responseStr.equalsIgnoreCase("notexists")) {
	        	
	    		HttpPost httpPostDeviceCreate = new HttpPost("https://www.doc.ic.ac.uk/~gljones/openbattery/device-create.php");
	     		
	     		List<NameValuePair> nameValuePairsDeviceCreate = new ArrayList<NameValuePair>(2);
	     		nameValuePairsDeviceCreate.add(new BasicNameValuePair("fromdevice", "1"));
	     		nameValuePairsDeviceCreate.add(new BasicNameValuePair("obversion", "1.0"));
	     		nameValuePairsDeviceCreate.add(new BasicNameValuePair("deviceid", deviceid));
	     		nameValuePairsDeviceCreate.add(new BasicNameValuePair("devicemodel", android.os.Build.MODEL));
	     		nameValuePairsDeviceCreate.add(new BasicNameValuePair("manufacturer", android.os.Build.MANUFACTURER));
	     		nameValuePairsDeviceCreate.add(new BasicNameValuePair("model", android.os.Build.MODEL));
	     		nameValuePairsDeviceCreate.add(new BasicNameValuePair("technology", android.os.BatteryManager.EXTRA_TECHNOLOGY));
	     		
	     		Integer scale=0;
	     		try {
	     			scale = Integer.parseInt(android.os.BatteryManager.EXTRA_SCALE);
	     		} catch (NumberFormatException e) {
	     			scale = 0;
	     		}
	     		
	     		nameValuePairsDeviceCreate.add(new BasicNameValuePair("designcapacity", scale.toString()));
	     		httpPostDeviceCreate.setEntity(new UrlEncodedFormEntity(nameValuePairsDeviceCreate));
				
     			HttpResponse responseDeviceCreate = httpclient.execute(httpPostDeviceCreate);

		        String responseStrDeviceCreate = URLEncoder.encode(org.apache.http.util.EntityUtils.toString(responseDeviceCreate.getEntity())).trim();

		        //Log.d("openbattery","deviceadded? "+ responseStrDeviceCreate);
		        
		        if (responseStrDeviceCreate.equalsIgnoreCase("deviceregistrationsuccessful")){
		        
		        	// New device linked to this user's account
		        	
		        	Toast.makeText(getApplicationContext(), "New device registered", Toast.LENGTH_SHORT);
		    		editor.putString("deviceRegistered","1");
		    		editor.commit();
		        	
		        }

			}
        
		} catch (Exception e) {
			// TODO
		}
			
	}
	
	private void logBattery(Intent batteryChangeIntent) {
		if (batteryChangeIntent == null)
			return;
		try {
			FileWriter out = null;
			if (mBatteryLogFile != null) {
				try {
					out = new FileWriter(mBatteryLogFile, true);
				} catch (Exception e) {
				}
			}
			if (out == null) {
				File root = Environment.getExternalStorageDirectory();
				if (root == null)
					throw new Exception("external storage dir not found");
				mBatteryLogFile = new File(root, OpenBatteryService.LOGFILEPATH);
				boolean fileExists = mBatteryLogFile.exists();
				if (!fileExists) {
					mBatteryLogFile.getParentFile().mkdirs();
					mBatteryLogFile.createNewFile();
				}
				if (!mBatteryLogFile.exists()) {
					throw new Exception("creation of file '"
							+ mBatteryLogFile.toString() + "' failed");
				}
				if (!mBatteryLogFile.canWrite())
					throw new Exception("file '" + mBatteryLogFile.toString()
							+ "' is not writable");
				out = new FileWriter(mBatteryLogFile, true);
				// if (!fileExists) {
				// String header = createHeadLine();
				// out.write(header);
				// out.write("\n");
				// }
			}
			if (mLastBatteryIntent != null) {
				String extras = createBatteryInfoLine(mLastBatteryIntent);
				out.write(extras);
				out.write("\n");
			}
			out.flush();
			out.close();
		} catch (Exception e) {
		}
	}

	// private String createHeadLine() {
	// StringBuffer result = new StringBuffer();
	// result.append("Nr;TimeMillis");
	// for (String key : batteryExtraKeys)
	// result.append(";").append(key);
	// return result.toString();
	// }

	private String createBatteryInfoLine(Intent batteryIntent) {

		// Format date including time zone when logging
		Date timeTextDate = new Date(System.currentTimeMillis());
		String timeText = textDate.format(timeTextDate);

		StringBuffer result = new StringBuffer();
		result.append(timeText);
		Bundle extras = batteryIntent.getExtras();
		for (String key : batteryExtraKeys)
			result.append(";").append(extras.get(key));
		return result.toString();
	}

	/**
	 * The function that runs in our worker thread
	 */
	Runnable mTask = new Runnable() {

		public void run() {
			mThreadRunning = true;

			while (!mQuitThread) {
				logBattery(mLastBatteryIntent);
				synchronized (OpenBatteryService.this) {
					try {
						OpenBatteryService.this.wait();
					} catch (Exception ignore) {
					}
				}
			}
			mThreadRunning = false;
			logBattery(mLastBatteryIntent);

		}

	};

	public void notifyService() {
		synchronized (OpenBatteryService.this) {
			OpenBatteryService.this.notifyAll();
		}
	}

	private SimpleDateFormat textDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss.SSS Z");

	public void makeDeviceId() {
		
		SharedPreferences settings = getSharedPreferences(OpenBattery.PREFS_NAME, 0);

		String deviceid = Long.toHexString(Double.doubleToLongBits(Math.random()));

		SharedPreferences.Editor editor = settings.edit();
		editor.putString("deviceid",deviceid);
		editor.commit();
		
	}
	
	static String submitData(String deviceid) {

		// assume deviceid is valid
		
		// Send file
		// From http://stackoverflow.com/questions/4126625/

		String url = "https://www.doc.ic.ac.uk/~gljones/openbattery/device-insert-data-file.php";
		File file = new File(Environment.getExternalStorageDirectory(),
				OpenBatteryService.LOGFILEPATH);

		Long fileLength = file.length();
		
		if (fileLength < 10) {
			
			return "blank";
			
		}
		
		// From http://stackoverflow.com/questions/4562344/

		try {
			HttpClient httpclient = new DefaultHttpClient();

			HttpPost httppost = new HttpPost(url);

			// Read the file

			BufferedReader br = new BufferedReader(new FileReader(file));

			String line;
			String fileContents = "";
			int i = 1;

			boolean keepSubmitting = true;
			List<NameValuePair> nameValuePairsSubmitFile = new ArrayList<NameValuePair>(
					2);

			while (((line = br.readLine()) != null) && (keepSubmitting == true)) {

				String amendedLine = line.replace(';',' ');
				fileContents = fileContents.concat(amendedLine);
				fileContents = fileContents.concat("\n");

				i++;

				if (i % 1000 == 0) {

					// Every 100000 lines send the data (if successful) continue
					// and reset the data;

					nameValuePairsSubmitFile.clear();
					nameValuePairsSubmitFile.add(new BasicNameValuePair(
							"fromdevice", "1"));
					nameValuePairsSubmitFile.add(new BasicNameValuePair(
							"obversion", "2.0"));
					
					nameValuePairsSubmitFile.add(new BasicNameValuePair("deviceid",
							deviceid));
					
					nameValuePairsSubmitFile.add(new BasicNameValuePair(
							"data-file", fileContents));
					httppost.setEntity(new UrlEncodedFormEntity(
							nameValuePairsSubmitFile));

					HttpResponse response = httpclient.execute(httppost);

					String responseStr = URLEncoder.encode(
							org.apache.http.util.EntityUtils.toString(response
									.getEntity())).trim();

					//Log.e("openbattery", "response " + responseStr);

					if (!responseStr.equalsIgnoreCase("insertfilesuccessful")) {

						// stop submitting if not successful
						keepSubmitting = false;

					}

					fileContents = "";

				} // end if
			} // end while

			// Submit remaining data, clear data file if successful

			nameValuePairsSubmitFile.clear();
			nameValuePairsSubmitFile.add(new BasicNameValuePair("fromdevice",
					"1"));
			nameValuePairsSubmitFile.add(new BasicNameValuePair("obversion",
					"2.0"));
			
			Log.d("openbattery","sending with deviceid " +deviceid);
			
			nameValuePairsSubmitFile.add(new BasicNameValuePair("deviceid",
					deviceid));
			nameValuePairsSubmitFile.add(new BasicNameValuePair("data-file",
					fileContents));
			httppost.setEntity(new UrlEncodedFormEntity(
					nameValuePairsSubmitFile));

			HttpResponse response = httpclient.execute(httppost);

			String responseStr = URLEncoder.encode(
					org.apache.http.util.EntityUtils.toString(response
							.getEntity())).trim();

			Log.e("openbattery", "response " + responseStr);

			if (responseStr.equalsIgnoreCase("insertfilesuccessful") && keepSubmitting) {

				// on success insert, delete file
				Log.e("openbattery", "recs inserted successfully");

				file.delete();
				file.createNewFile();
				return "insertsuccessful";

			} else {

				if (responseStr.equalsIgnoreCase("blank")) {

					// blank entry
					Log.e("openbattery", "blank file, no data to submit");

					return "blank";
					
				} else {
				
				// not successful
				Log.e("openbattery", "recs not inserted successfuly");
				return "error";
				}
			}

		} catch (Exception e) {
			// show error
			return "error";
		}

	}

}
